#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <dlfcn.h>
#include <math.h>

static char *invoke_err(char *args)
{
	return NULL;
}

char *(*invoke)(char *) = invoke_err;

typedef struct {
	ngx_str_t output_words;
} ngx_http_ath_sower_loc_conf_t;

// To process HelloWorld command arguments
static char* ngx_http_ath_sower(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);

// Allocate memory for HelloWorld command
static void* ngx_http_ath_sower_create_loc_conf(ngx_conf_t* cf);

// Copy HelloWorld argument to another place
static char* ngx_http_ath_sower_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child);

// Structure for the HelloWorld command
static ngx_command_t ngx_http_ath_sower_commands[] = {
	{
		ngx_string("ath_sower"), // The command name
		NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
		ngx_http_ath_sower, // The command handler
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_ath_sower_loc_conf_t, output_words),
		NULL
	},
	ngx_null_command
};

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_ath_sower_module_ctx = {
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	ngx_http_ath_sower_create_loc_conf,
	ngx_http_ath_sower_merge_loc_conf
};

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_ath_sower_module = {
	NGX_MODULE_V1,
	&ngx_http_ath_sower_module_ctx,
	ngx_http_ath_sower_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	NULL,
	NULL, NULL, NULL,
	NULL,
	NGX_MODULE_V1_PADDING
};

static char *ath_build_request(ngx_http_request_t *r)
{
	size_t req_size;
	char *p = NULL;
	
	if(!r->request_start || strncmp((char *)r->request_start,(char*)"POST",4)){
		return NULL;
	}

	req_size = atoi((char *)r->headers_in.content_length->value.data);
	if(req_size+1){
		p = malloc(req_size);
		if(!p){
			return NULL;
		}
		bzero(p, req_size+1);
	}
	strncpy(p, (char *)r->header_in->pos, req_size);
	return p;
}

static int 
ath_build_response(ngx_http_request_t *r, ngx_chain_t *out, char *res)
{
	ngx_buf_t* b;
	
	b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
	b->pos = (u_char*)res;
	b->last = b->pos + strlen(res);
	b->memory = 1;
	b->last_buf = 1;
	out->buf = b;
	out->next = NULL;

	return 0;
}

static void build_headers_out(ngx_http_request_t *r, char *response, int status)
{
	r->headers_out.content_type.len = strlen("text/plain");
	r->headers_out.content_type.data = (u_char *)"text/plain";
	r->headers_out.status = status;
	r->headers_out.content_length_n = strlen(response);
}

static ngx_int_t 
ngx_http_ath_sower_handler(ngx_http_request_t* r) 
{
	char *response = NULL;
	char *request = NULL;
	ngx_int_t rc;
	ngx_chain_t out;
	ngx_int_t ret;
	int status;

	bzero(&out, sizeof(out));
	request = ath_build_request(r);
	if(!request){
		status = NGX_HTTP_INTERNAL_SERVER_ERROR;
		response = "build request error!!";
		goto response_client;
	}
	
	printf("file:%s,line:%d,func:%s,request[%s]\n",__FILE__,__LINE__,__func__,request); 
	response = invoke(request);
	if(!response){
		status = NGX_HTTP_INTERNAL_SERVER_ERROR;
		response = "invoke error!!";
	}
	
response_client:
	
	ath_build_response(r, &out, response);
	build_headers_out(r, response, status);

	rc = ngx_http_send_header(r);
	if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
		return rc;
	}

	ret = ngx_http_output_filter(r, &out);
	if(status == NGX_HTTP_OK){
		free(request);
		free(response);
	}
	
	return ret;
}

static void* ngx_http_ath_sower_create_loc_conf(ngx_conf_t* cf) {
	ngx_http_ath_sower_loc_conf_t* conf;

	conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ath_sower_loc_conf_t));
	if (conf == NULL) {
		return NGX_CONF_ERROR;
	}
	conf->output_words.len = 0;
	conf->output_words.data = NULL;

	return conf;
}

static char* ngx_http_ath_sower_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) {
	ngx_http_ath_sower_loc_conf_t* prev = parent;
	ngx_http_ath_sower_loc_conf_t* conf = child;
	ngx_conf_merge_str_value(conf->output_words, prev->output_words, "Nginx");
	return NGX_CONF_OK;
}

static char* ngx_http_ath_sower(ngx_conf_t* cf, ngx_command_t* cmd, void* conf) {
	ngx_http_core_loc_conf_t* clcf;
	void *handle;

	printf("file:%s,line:%d,func:%s\n",__FILE__,__LINE__,__func__); 
	handle = dlopen("libm.so", RTLD_NOW|RTLD_GLOBAL);
	handle = dlopen("libinvokeproxy.so", RTLD_NOW|RTLD_GLOBAL);
	if(!handle){
		printf("file:%s,line:%d,func:%s,err[%s]\n",__FILE__,__LINE__,__func__,dlerror()); 
		return NGX_CONF_ERROR;
	}
	invoke = dlsym(handle,"invoke");

	clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	clcf->handler = ngx_http_ath_sower_handler;
	ngx_conf_set_str_slot(cf, cmd, conf);
	return NGX_CONF_OK;
}
